home *** CD-ROM | disk | FTP | other *** search
-
- /* @(#)in.reved.c 1.5 91/11/13
- *
- * Reve network daemon for remote games with user@host
- * These routines are based on those found in the BSD in.talkd program,
- * which is:
- *
- * Copyright (c) 1983 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that: (1) source distributions retain this entire copyright
- * notice and comment, and (2) distributions including binaries display
- * the following acknowledgement: ``This product includes software
- * developed by the University of California, Berkeley and its contributors''
- * in the documentation or other materials provided with the distribution
- * and in all advertising materials mentioning features or use of this
- * software. Neither the name of the University nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- #include "reve.h"
- #include <errno.h>
- #include <syslog.h>
- #include <utmp.h>
- #include <sgtty.h>
- #include <sys/stat.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <sys/wait.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include "ctl.h"
-
- #define MAX_ID 16000 /* << 2^15 so no sign troubles. */
- #define NIL ((TABLE_ENTRY *) 0)
- #define N_LINES 5
- #define N_CHARS 120
- #define SMAX(a, b) ((a) > (b) ? (a) : (b))
- #define SWAPSHORT(a) (((a << 8) | ((unsigned short) a >> 8)) & 0xffff)
- #define SWAPLONG(a) ((SWAPSHORT(a)<<16) | (SWAPSHORT(((unsigned) a >> 16))))
-
- extern char *malloc() ;
- extern int errno ;
-
- struct hostent *gethostbyaddr() ;
- struct timeval tp ;
-
- typedef struct table_entry TABLE_ENTRY ;
-
- struct table_entry {
- CTL_MSG request ;
- long time ;
- TABLE_ENTRY *next ;
- TABLE_ENTRY *last ;
- } ;
-
- TABLE_ENTRY *table = NIL ;
-
- CTL_MSG *find_match(), *find_request(), request, swapmsg() ;
- CTL_RESPONSE response ;
-
- char debugname[MAXLINE] ;
- char hostname[MAXLINE] ;
-
- FILE *dfd = stdout ;
-
- int debug = 0 ;
- int nofork = 0 ;
-
-
- /*ARGSUSED*/
- main(argc, argv)
- int argc ;
- char *argv[] ;
- {
- struct sockaddr_in from ;
- int from_size ;
- int cc ;
- int name_length = sizeof(hostname) ;
- struct timeval tv ;
-
- #ifdef NO_43SELECT
- int rfds ;
- #else
- fd_set rfds ;
- #endif /*NO_43SELECT*/
-
- if (debug)
- {
- SPRINTF(debugname, "/tmp/reved.debug%D", getpid()) ;
- dfd = fopen(debugname, "w") ;
- }
-
- openlog("reved", LOG_PID, LOG_DAEMON) ;
-
- gethostname(hostname, &name_length) ;
-
- for (;;)
- {
- tv.tv_sec = MAX_LIFE ;
- tv.tv_usec = 0 ;
-
- #ifdef NO_43SELECT
- rfds = 1 << 0 ;
- #else
- FD_ZERO(&rfds) ;
- FD_SET(0, &rfds) ;
- #endif /*NO_43SELECT*/
-
- #ifdef NO_43SELECT
- if (select(32, &rfds, 0, 0, &tv) <= 0)
- #else
- if (select(FD_SETSIZE, &rfds, (fd_set *) 0, (fd_set *) 0, &tv) <= 0)
- #endif /*NO_43SELECT*/
- exit(0) ;
-
- from_size = sizeof(from) ;
- cc = recvfrom(0, (char *) &request, sizeof(request), 0,
- &from, &from_size) ;
-
- if (cc != sizeof(request))
- {
- if (cc < 0 && errno != EINTR)
- syslog(LOG_ERR, "recvfrom: %m") ;
- }
- else
- {
- if (debug) FPRINTF(dfd, "Request received : \n") ;
- if (debug) print_request(&request) ;
-
- request = swapmsg(request) ;
- process_request(&request, &response) ;
-
- if (debug) FPRINTF(dfd, "Response sent : \n") ;
- if (debug) print_response(&response) ;
-
- /* Can block here. */
-
- cc = sendto(0, (char *) &response, sizeof(response), 0,
- &request.ctl_addr, sizeof(request.ctl_addr)) ;
-
- if (cc != sizeof(response)) syslog(LOG_ERR, "sendto: %m") ;
- }
- }
- }
-
-
- /* Because the tty driver insists on attaching a terminal-less process to
- * any terminal that it writes on, we must fork a child to protect ourselves.
- */
-
- announce(request, remote_machine)
- CTL_MSG *request ;
- char *remote_machine ;
- {
- int pid, val, status ;
-
- if (nofork) return(announce_proc(request, remote_machine)) ;
-
- if (pid = fork())
- {
-
- /* We are the parent, so wait for the child. */
-
- if (pid == -1) return(FAILED) ; /* The fork failed. */
-
- do
- {
- val = wait(&status) ;
- if (val == -1)
- {
- if (errno == EINTR) continue;
- else
- {
- syslog(LOG_ERR, "wait: %m") ; /* Shouldn't happen. */
- return(FAILED) ;
- }
- }
- }
- while (val != pid) ;
-
- if (status & 0377 > 0) return(FAILED) ; /* Killed by some signal. */
-
- /* Get the second byte, this is the exit/return code. */
-
- return((status >> 8) & 0377) ;
-
- }
- else /* we are the child, go and do it. */
- _exit(announce_proc(request, remote_machine)) ;
- }
-
-
- /* See if the user is accepting messages. If so, announce that
- * a talk is requested.
- */
-
- announce_proc(request, remote_machine)
- CTL_MSG *request ;
- char *remote_machine ;
- {
- int pid, status ;
- char full_tty[32] ;
- FILE *tf ;
- struct stat stbuf ;
-
- SPRINTF(full_tty, "/dev/%s", request->r_tty) ;
-
- if (access(full_tty, 0) != 0) return(FAILED) ;
- if ((tf = fopen(full_tty, "w")) == NULL) return(PERMISSION_DENIED) ;
-
- /* Open gratuitously attaches the reved to any tty it opens, so disconnect
- * us from the tty before we catch a signal.
- */
-
- #ifndef hpux
- IOCTL(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0) ;
- #endif /*hpux*/
-
- if (fstat(fileno(tf), &stbuf) < 0) return(PERMISSION_DENIED) ;
- if ((stbuf.st_mode & 020) == 0) return(PERMISSION_DENIED) ;
-
- print_mesg(tf, request, remote_machine) ;
- FCLOSE(tf) ;
- return(SUCCESS) ;
- }
-
-
- delete(ptr) /* Classic delete from a double-linked list. */
- TABLE_ENTRY *ptr ;
- {
- if (debug) FPRINTF(dfd, "Deleting : ") ;
- if (debug) print_request(&ptr->request) ;
-
- if (table == ptr) table = ptr->next ;
- else if (ptr->last != NIL) ptr->last->next = ptr->next ;
-
- if (ptr->next != NIL) ptr->next->last = ptr->last ;
- (void) free((char *) ptr) ;
- }
-
-
- delete_invite(id_num) /* Delete the invitation with id 'id_num'. */
- int id_num ;
- {
- TABLE_ENTRY *ptr ;
-
- ptr = table ;
-
- if (debug) FPRINTF(dfd, "Entering delete_invite with %d\n", id_num) ;
-
- while (ptr != NIL && ptr->request.id_num != id_num)
- {
- if (debug) print_request(&ptr->request) ;
- ptr = ptr->next ;
- }
-
- if (ptr != NIL)
- {
- delete(ptr) ;
- return(SUCCESS) ;
- }
- return(NOT_HERE) ;
- }
-
-
- do_announce(request, response)
- CTL_MSG *request ;
- CTL_RESPONSE *response ;
- {
- struct hostent *hp ;
- CTL_MSG *ptr ;
- int result ;
-
- /* See if the user is logged in. */
-
- result = find_user(request->r_name, request->r_tty) ;
-
- if (result != SUCCESS)
- {
- response->answer = result ;
- return ;
- }
-
- hp = gethostbyaddr(&request->ctl_addr.sin_addr,
- sizeof(struct in_addr), AF_INET) ;
-
- if (hp == (struct hostent *) 0)
- {
- response->answer = MACHINE_UNKNOWN ;
- return ;
- }
-
- ptr = find_request(request) ;
- if (ptr == (CTL_MSG *) 0)
- {
- insert_table(request, response) ;
- response->answer = announce(request, hp->h_name) ;
- }
- else if (request->id_num > ptr->id_num)
- {
-
- /* This is an explicit re-announce, so update the id_num
- * field to avoid duplicates and re-announce the reve request.
- */
-
- ptr->id_num = response->id_num = new_id() ;
- response->answer = announce(request, hp->h_name) ;
- }
- else
- {
- response->id_num = ptr->id_num ; /* Duplicated request, ignore it. */
- response->answer = SUCCESS ;
- }
- return ;
- }
-
-
- /* Look in the table for an invitation that matches the current
- * request looking for an invitation.
- */
-
- CTL_MSG *
- find_match(request)
- CTL_MSG *request ;
- {
- TABLE_ENTRY *ptr ;
- long current_time ;
-
- gettimeofday(&tp, (struct timezone *) 0) ;
- current_time = tp.tv_sec ;
-
- ptr = table ;
-
- if (debug)
- {
- FPRINTF(dfd, "Entering Look-Up with : \n") ;
- print_request(request) ;
- }
-
- while (ptr != NIL)
- {
- if ((ptr->time - current_time) > MAX_LIFE)
- {
-
- /* The entry is too old. */
-
- if (debug) FPRINTF(dfd, "Deleting expired entry : \n") ;
- if (debug) print_request(&ptr->request) ;
- delete(ptr) ;
- ptr = ptr->next ;
- continue ;
- }
-
- if (debug) print_request(&ptr->request) ;
-
- if (strcmp(request->l_name, ptr->request.r_name) == 0 &&
- strcmp(request->r_name, ptr->request.l_name) == 0 &&
- ptr->request.type == LEAVE_INVITE)
- return(&ptr->request) ;
-
- ptr = ptr->next ;
- }
- return((CTL_MSG *) 0) ;
- }
-
-
- /* Look for an identical request, as opposed to a complimentary
- * one as find_match does.
- */
-
- CTL_MSG *
- find_request(request)
- CTL_MSG *request ;
- {
- TABLE_ENTRY *ptr ;
- long current_time ;
-
- gettimeofday(&tp, (struct timezone *) 0) ;
- current_time = tp.tv_sec ;
-
- /* See if this is a repeated message, and check for out of date entries
- * in the table while we are it.
- */
-
- ptr = table ;
-
- if (debug)
- {
- FPRINTF(dfd, "Entering find_request with : \n") ;
- print_request(request) ;
- }
-
- while (ptr != NIL)
- {
- if ((ptr->time - current_time) > MAX_LIFE)
- {
-
- /* The entry is too old. */
-
- if (debug) FPRINTF(dfd, "Deleting expired entry : \n") ;
- if (debug) print_request(&ptr->request) ;
- delete(ptr) ;
- ptr = ptr->next ;
- continue ;
- }
-
- if (debug) print_request(&ptr->request) ;
-
- if (strcmp(request->r_name, ptr->request.r_name) == 0 &&
- strcmp(request->l_name, ptr->request.l_name) == 0 &&
- request->type == ptr->request.type &&
- request->pid == ptr->request.pid)
- {
-
- /* Update the time if we 'touch' it. */
-
- ptr->time = current_time ;
- return(&ptr->request) ;
- }
- ptr = ptr->next ;
- }
- return((CTL_MSG *) 0) ;
- }
-
-
- find_user(name, tty) /* Search utmp for the local user. */
- char *name, *tty ;
- {
- struct utmp ubuf ;
- int fd ;
-
- if ((fd = open("/etc/utmp", 0)) == -1)
- {
- syslog(LOG_ERR, "Can't open /etc/utmp: %m") ;
- return(FAILED) ;
- }
-
- while (read(fd, (char *) &ubuf, sizeof(ubuf)) == sizeof(ubuf))
- {
- if (strncmp(ubuf.ut_name, name, sizeof(ubuf.ut_name)) == 0)
- {
- if (*tty == '\0')
- {
-
- /* No particular tty was requested. */
-
- STRCPY(tty, ubuf.ut_line) ;
- CLOSE(fd) ;
- return(SUCCESS) ;
- }
- else if (strcmp(ubuf.ut_line, tty) == 0)
- {
- CLOSE(fd) ;
- return(SUCCESS) ;
- }
- }
- }
- CLOSE(fd) ;
- return(NOT_HERE) ;
- }
-
-
- insert_table(request, response)
- CTL_MSG *request ;
- CTL_RESPONSE *response ;
- {
- TABLE_ENTRY *ptr ;
- long current_time ;
-
- gettimeofday(&tp, (struct timezone *) 0) ;
- current_time = tp.tv_sec ;
-
- response->id_num = request->id_num = new_id() ;
-
- /* Insert a new entry into the top of the list. */
-
- ptr = (TABLE_ENTRY *) malloc(sizeof(TABLE_ENTRY)) ;
-
- if (ptr == NIL) syslog(LOG_ERR, "malloc in insert_table") ;
-
- ptr->time = current_time ;
- ptr->request = *request ;
-
- ptr->next = table ;
- if (ptr->next != NIL) ptr->next->last = ptr ;
- ptr->last = NIL ;
- table = ptr ;
- }
-
-
- new_id() /* Generate a unique non-zero sequence number. */
- {
- static int current_id = 0 ;
-
- current_id = (current_id + 1) % MAX_ID ;
-
- /* 0 is reserved, helps to pick up bugs. */
-
- if (current_id == 0) current_id = 1 ;
- return(current_id) ;
- }
-
-
- /* Build a block of characters containing the message.
- * It is sent blank filled and in a single block to try to keep the message
- * in one piece if the recipient in in vi at the time.
- */
-
- print_mesg(tf, request, remote_machine)
- FILE *tf ;
- CTL_MSG *request ;
- char *remote_machine ;
- {
- struct timeval clock ;
- struct tm *localclock, *localtime() ;
- char big_buf[N_LINES*N_CHARS], line_buf[N_LINES][N_CHARS] ;
- char *bptr, *lptr ;
- int i, j, max_size, sizes[N_LINES] ;
-
- i = 0 ;
- max_size = 0 ;
-
- gettimeofday(&clock, (struct timezone *) NULL) ;
- localclock = localtime(&clock.tv_sec) ;
-
- SPRINTF(line_buf[i], " ") ;
-
- sizes[i] = strlen(line_buf[i]) ;
- max_size = SMAX(max_size, sizes[i]) ;
- i++ ;
-
- SPRINTF(line_buf[i], "Message from Reve_Daemon@%s at %d:%02d ...",
- hostname, localclock->tm_hour , localclock->tm_min) ;
-
- sizes[i] = strlen(line_buf[i]) ;
- max_size = SMAX(max_size, sizes[i]) ;
- i++ ;
-
- SPRINTF(line_buf[i], "reve: connection requested by %s@%s.",
- request->l_name, remote_machine) ;
-
- sizes[i] = strlen(line_buf[i]) ;
- max_size = SMAX(max_size, sizes[i]) ;
- i++ ;
-
- SPRINTF(line_buf[i], "reve: respond with: reve -opponent %s@%s",
- request->l_name, remote_machine) ;
-
- sizes[i] = strlen(line_buf[i]) ;
- max_size = SMAX(max_size, sizes[i]) ;
- i++ ;
-
- SPRINTF(line_buf[i], " ") ;
-
- sizes[i] = strlen(line_buf[i]) ;
- max_size = SMAX(max_size, sizes[i]) ;
- i++ ;
-
- bptr = big_buf ;
- *(bptr++) = '\007' ; /* Send something to wake them up. */
- *(bptr++) = '\r' ; /* Add a \r in case of raw mode. */
- *(bptr++) = '\n' ;
- for (i = 0; i < N_LINES; i++)
- {
-
- /* Copy the line into the big buffer. */
-
- lptr = line_buf[i] ;
- while (*lptr != '\0') *(bptr++) = *(lptr++) ;
-
- /* Pad out the rest of the lines with blanks. */
-
- for (j = sizes[i]; j < max_size + 2; j++) *(bptr++) = ' ' ;
-
- *(bptr++) = '\r' ; /* Add a \r in case of raw mode. */
- *(bptr++) = '\n' ;
- }
- *bptr = '\0' ;
-
- FPRINTF(tf, big_buf) ;
- FFLUSH(tf) ;
- #ifndef hpux
- IOCTL(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0) ;
- #endif /*hpux*/
- }
-
-
- print_request(request)
- CTL_MSG *request ;
- {
- FPRINTF(dfd, "type is %d, l_user %s, r_user %s, r_tty %s\n\tid = %d\n",
- request->type, request->l_name, request->r_name,
- request->r_tty, request->id_num) ;
- FFLUSH(dfd) ;
- }
-
-
- print_response(response)
- CTL_RESPONSE *response;
- {
- FPRINTF(dfd, "type is %d, answer is %d, id = %d\n\n", response->type,
- response->answer, response->id_num) ;
- FFLUSH(dfd) ;
- }
-
-
- process_request(request, response)
- CTL_MSG *request ;
- CTL_RESPONSE *response ;
- {
- CTL_MSG *ptr ;
-
- response->type = request->type ;
- response->id_num = 0 ;
-
- switch (request->type)
- {
- case ANNOUNCE : do_announce(request, response) ;
- break ;
- case LEAVE_INVITE : ptr = find_request(request) ;
- if (ptr != (CTL_MSG *) 0)
- {
- response->id_num = ptr->id_num ;
- response->answer = SUCCESS ;
- }
- else insert_table(request, response) ;
- break ;
- case LOOK_UP : ptr = find_match(request) ;
- if (ptr != (CTL_MSG *) 0)
- {
- response->id_num = ptr->id_num ;
- response->addr = ptr->addr ;
- response->dtype = ptr->dtype ;
- response->answer = SUCCESS ;
- }
- else response->answer = NOT_HERE ;
- break ;
- case DELETE : response->answer = delete_invite(request->id_num) ;
- break ;
- default : response->answer = UNKNOWN_REQUEST ;
- }
- }
-
-
- CTL_MSG
- swapmsg(req) /* Heuristic to detect if need to swap bytes. */
- CTL_MSG req ;
- {
- CTL_MSG swapreq ;
-
- if (req.ctl_addr.sin_family == SWAPSHORT(AF_INET))
- {
- swapreq = req ;
- swapreq.id_num = SWAPLONG(req.id_num) ;
- swapreq.pid = SWAPLONG(req.pid) ;
- swapreq.addr.sin_family = SWAPSHORT(req.addr.sin_family) ;
- swapreq.ctl_addr.sin_family = SWAPSHORT(req.ctl_addr.sin_family) ;
- return(swapreq) ;
- }
- else return(req) ;
- }
-